package org.unidata.mdm.dq.core.type.model.source;

import java.io.Serializable;
import java.util.Arrays;
import java.util.Collection;
import java.util.EnumSet;
import java.util.Objects;
import java.util.Set;
import java.util.function.Supplier;

import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.unidata.mdm.dq.core.type.cleanse.CleanseFunctionPortFilteringMode;
import org.unidata.mdm.dq.core.type.cleanse.CleanseFunctionPortInputType;
import org.unidata.mdm.dq.core.type.cleanse.CleanseFunctionPortValueType;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;


/**
 * I/O port description.
 */
public class CleanseFunctionPort implements Serializable {
    /**
     * GSVUID.
     */
    private static final long serialVersionUID = 2037491496786146156L;

    @JacksonXmlProperty(isAttribute = true)
    protected String name;

    @JacksonXmlProperty(isAttribute = true)
    protected String displayName;

    @JacksonXmlProperty(isAttribute = true)
    protected String description;

    @JacksonXmlProperty(isAttribute = true)
    protected boolean required;

    @JacksonXmlProperty(isAttribute = true)
    protected CleanseFunctionPortFilteringMode filteringMode;

    @JacksonXmlElementWrapper(localName = "inputTypes")
    @JacksonXmlProperty(localName = "inputType")
    protected EnumSet<CleanseFunctionPortInputType> inputTypes;

    @JacksonXmlElementWrapper(localName = "valueTypes")
    @JacksonXmlProperty(localName = "valueType")
    protected EnumSet<CleanseFunctionPortValueType> valueTypes;

    // Not serializable section
    @JsonIgnore(true)
    protected transient Supplier<String> displayNameSupplier;

    @JsonIgnore(true)
    protected transient Supplier<String> descriptionSupplier;

    /**
     * Gets the value of the name property.
     *
     * @return
     *     possible object is
     *     {@link String }
     *
     */
    public String getName() {
        return name;
    }

    /**
     * Sets the value of the name property.
     *
     * @param value
     *     allowed object is
     *     {@link String }
     *
     */
    public void setName(String value) {
        this.name = value;
    }

    /**
     * @return the displayName
     */
    public String getDisplayName() {
        return Objects.nonNull(displayNameSupplier) ? displayNameSupplier.get() : displayName;
    }

    /**
     * @param displayName the displayName to set
     */
    public void setDisplayName(String displayName) {
        this.displayName = displayName;
    }

    /**
     * @param displayNameSupplier the displayNameSupplier to set
     */
    public void setDisplayName(Supplier<String> displayNameSupplier) {
        this.displayNameSupplier = displayNameSupplier;
    }

    /**
     * Gets the value of the description property.
     *
     * @return
     *     possible object is
     *     {@link String }
     *
     */
    public String getDescription() {
        return Objects.nonNull(descriptionSupplier) ? descriptionSupplier.get() : description;
    }

    /**
     * Sets the value of the description property.
     *
     * @param value
     *     allowed object is
     *     {@link String }
     *
     */
    public void setDescription(String value) {
        this.description = value;
    }

    /**
     * @param descriptionSupplier the descriptionSupplier to set
     */
    public void setDescription(Supplier<String> descriptionSupplier) {
        this.descriptionSupplier = descriptionSupplier;
    }

    /**
     * Gets the value of the required property.
     *
     */
    public boolean isRequired() {
        return required;
    }

    /**
     * Sets the value of the required property.
     *
     */
    public void setRequired(boolean value) {
        this.required = value;
    }

    /**
     * Gets the value of the applicationMode property.
     *
     * @return
     *     possible object is
     *     {@link CleanseFunctionPortFilteringMode }
     *
     */
    public CleanseFunctionPortFilteringMode getFilteringMode() {
        return Objects.isNull(filteringMode)
                ? CleanseFunctionPortFilteringMode.MODE_UNDEFINED
                : filteringMode;
    }

    /**
     * Sets the value of the applicationMode property.
     *
     * @param value
     *     allowed object is
     *     {@link CleanseFunctionPortFilteringMode }
     *
     */
    public void setFilteringMode(CleanseFunctionPortFilteringMode value) {
        this.filteringMode = value;
    }

    /**
     * Gets the value of the dataType property.
     *
     * @return
     *     possible object is
     *     {@link CleanseFunctionPortInputType }
     *
     */
    public Set<CleanseFunctionPortInputType> getInputTypes() {
        if (Objects.isNull(inputTypes)) {
            inputTypes = EnumSet.noneOf(CleanseFunctionPortInputType.class);
        }
        return inputTypes;
    }

    /**
     * Sets the value of the dataType property.
     *
     * @param value
     *     allowed object is
     *     {@link CleanseFunctionPortInputType }
     *
     */
    public void setInputTypes(Collection<CleanseFunctionPortInputType> value) {
        getInputTypes().clear();
        getInputTypes().addAll(value);
    }

    /**
     * @return the valueTypes
     */
    public Set<CleanseFunctionPortValueType> getValueTypes() {
        if (Objects.isNull(valueTypes)) {
            valueTypes = EnumSet.noneOf(CleanseFunctionPortValueType.class);
        }
        return valueTypes;
    }

    /**
     * @param valueTypes the valueTypes to set
     */
    public void setValueTypes(Collection<CleanseFunctionPortValueType> valueTypes) {
        getValueTypes().clear();
        getValueTypes().addAll(valueTypes);
    }

    public CleanseFunctionPort withName(String value) {
        setName(value);
        return this;
    }

    public CleanseFunctionPort withDisplayName(String value) {
        setDisplayName(value);
        return this;
    }

    public CleanseFunctionPort withDisplayName(Supplier<String> value) {
        setDisplayName(value);
        return this;
    }

    public CleanseFunctionPort withDescription(String value) {
        setDescription(value);
        return this;
    }

    public CleanseFunctionPort withDescription(Supplier<String> value) {
        setDescription(value);
        return this;
    }

    public CleanseFunctionPort withRequired(boolean value) {
        setRequired(value);
        return this;
    }

    public CleanseFunctionPort withFilteringMode(CleanseFunctionPortFilteringMode value) {
        setFilteringMode(value);
        return this;
    }

    public CleanseFunctionPort withInputTypes(CleanseFunctionPortInputType... value) {
        if (ArrayUtils.isNotEmpty(value)) {
            return withInputTypes(Arrays.asList(value));
        }
        return this;
    }

    public CleanseFunctionPort withInputTypes(Collection<CleanseFunctionPortInputType> value) {
        if (CollectionUtils.isNotEmpty(value)) {
            getInputTypes().addAll(value);
        }
        return this;
    }

    public CleanseFunctionPort withValueTypes(CleanseFunctionPortValueType... value) {
        if (ArrayUtils.isNotEmpty(value)) {
            return withValueTypes(Arrays.asList(value));
        }
        return this;
    }

    public CleanseFunctionPort withValueTypes(Collection<CleanseFunctionPortValueType> value) {
        if (CollectionUtils.isNotEmpty(value)) {
            getValueTypes().addAll(value);
        }
        return this;
    }
}
